
interface AttachmentEditDto {
    fileName: string;
    /** 文件路径 */
    filePath: string;
    /** 文件缩略图名称 */
    fileSLTName: string | undefined;
    /** 文件缩略图路径 */
    fileSLTPath: string | undefined;
}


export type itemType = "radio" | "ckeckBox" | "text" | "input" | "file" | "file-doc" | "file-image" | "file-video" | "blank" | 'text-content';
export interface ICustomFormItem {
    key?: string;
    isGroup: false;
    type: itemType; // "group" 类型可以通过导入也可以通过新建
    title: string;
    score?: number;
    isRequird?: boolean;
    supplement?: boolean; // 是否带有补充内容. itemType 为 "radio" or "ckeckBox" 时有效 且只能是最后一个选项开启
    supplementValue?: string;
    maxSelected: number;
    fileAccept: string;
    fileSize: number;
    fileLimit: number;
    files?: AttachmentEditDto[];
    value?: string | number;
    options: Array<{
        key: string
        title: string
        checked?: boolean;
        score?: number
    }>;
}

export interface IPrecondition {
    type: "score" | "selected"; // 前置条件类型 分数 or 选项
    // itemSourceKey: string; // 指定到某一个（一层）的 item 的 key
    children: ICustomFormListItem[]; // 计算通过时显示的子项
    scoreValueCalc: { // 按分数计算
        type: ">" | "<" | "="
        value: number
    } | undefined;
    OptionKey: { key: string } | undefined; // 选中某个选项 仅 itemSourceKey 指向的 item.isGroup == false
}
export interface ICustomFormListItem {
    key: string;
    isGroup: boolean;
    precondition?: Array<IPrecondition>;
    group?: ICustomFormGroup;
    item?: ICustomFormItem;
}

export interface ICustomFormGroup {
    key?: string;
    isGroup: true;
    groupName: string | "default"; // default是每个表单必须的最外层
    maxScore?: number | undefined;
    isImport?: boolean;
    importOption?: {
        id: number,
        number: number,
        name: string
    };
    score?: number;
    itemList: Array<ICustomFormListItem>;
}
export class CustomFormItem implements ICustomFormItem {
    constructor(t: itemType) {
        this.type = t;
        this.title = '新标题';
        if (this.type === 'file-doc') {
            this.fileAccept += 'application/msword';
            this.fileAccept += ',application/pdf,application/vnd.ms-powerpoint,application/vnd.ms-works';
            this.fileAccept += ',application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
        }
    }
    key?: string;
    isGroup: false = false;
    type: itemType;
    title: string;
    score = 0;
    isRequird = false;
    supplement?: boolean;
    supplementValue = '';
    maxSelected = 0;
    value: string | number = ''; // 类才有的属性，用于绑定输入框值
    files: AttachmentEditDto[] = [];
    fileAccept = 'image/*';
    fileSize = 1024;
    fileLimit = 1;
    options: {
        key: string;
        title: string;
        checked: boolean;
        disable: boolean;
        // 是否带有补充内容. itemType 为 "radio" or "ckeckBox" 时有效 且只能是最后一个选项开启
        score?: number;
    }[] = [];

    static fromJson(data: ICustomFormItem): CustomFormItem {
        const item = new CustomFormItem(data.type);
        Object.assign(item, data);
        item.options.forEach(v => v.checked = v.checked || false);
        if (item.files.length) {
            item.files.forEach(v => Object.assign(v, { name: v.fileName, status: 'done', url: AppConsts.remoteServiceBaseUrl + v.filePath, uid: v.id }));
        }
        return item;
    }

    removeOption(key: string, itemKey: String, parentGroup: CustomFormGroup) {
        parentGroup.itemList.forEach(v => {
            if (v.key === itemKey) {
                if (v.precondition) {
                    // tslint:disable-next-line: no-use-before-declare
                    Precondition.removeprecond(v.precondition, { optionKey: key });
                }
            }
        });
        this.options.forEach((v, i) => {
            if (v.key === key) this.options.splice(i, 1);
        });
        this.reSetKey();
    }
    addOption() {
        if (!this.options) this.options = [];
        this.maxSelected += 1;
        this.options.push({
            key: '',
            title: '新选项',
            disable: false,
            checked: false
        });
        this.reSetKey();
    }
    reSetKey() {
        this.options.forEach((v, i) => {
            v.key = this.key + '.' + i.toString().padStart(3, '0');
        });
    }
    toJson(hasValue = false): ICustomFormItem {
        return {
            key: this.key,
            isGroup: this.isGroup,
            type: this.type,
            title: this.title,
            isRequird: this.isRequird,
            supplement: this.supplement,
            supplementValue: this.supplementValue,
            maxSelected: this.maxSelected,
            fileAccept: this.fileAccept,
            files: hasValue && this.files.length ? this.files : undefined,
            value: hasValue ? this.value : undefined,
            fileSize: this.fileSize,
            fileLimit: this.fileLimit,
            options: this.options.map(a => ({ title: a.title, key: a.key, score: a.score, checked: hasValue ? a.checked : undefined }))
        };
    }
}
export class Precondition implements IPrecondition {
    constructor(type: "score" | "selected", source: CustomFormGroup | CustomFormItem, optionKey?: string) {
        this.sourceItem = source;
        this.type = type;
        if (!source.isGroup && optionKey) {
            this.OptionKey = (<CustomFormItem>source).options.find(f => f.key === optionKey);
        }
    }
    type: "score" | "selected";
    sourceItem: CustomFormGroup | CustomFormItem;
    children: CustomFormListItem[];
    scoreValueCalc: { type: ">" | "<" | "="; value: number; };
    OptionKey: { key: string, checked: boolean } | undefined;

    static fromJson(data: IPrecondition, source: CustomFormGroup | CustomFormItem): Precondition {
        const pre = new Precondition(data.type, source, data.OptionKey ? data.OptionKey.key : undefined);
        return Object.assign(pre, data, {
            sourceItem: source, children: data.children.map(v => v.isGroup ?
                // tslint:disable-next-line: no-use-before-declare
                new CustomFormListItem(CustomFormGroup.fromJson(v.group)) :
                // tslint:disable-next-line: no-use-before-declare
                new CustomFormListItem(CustomFormItem.fromJson(v.item)))
        });
    }
    static removeprecond(preconditions: Precondition[], delPrems: {
        optionKey?: string,
        sourceItem?: CustomFormGroup | CustomFormItem,
        scoreValueCalc?: { type: ">" | "<" | "="; value: number; }
    }): boolean {
        let del = false;
        for (let idx = 0; idx < preconditions.length; idx++) {
            const precondition = preconditions[idx];
            if (delPrems.optionKey) {
                if (precondition.OptionKey.key === delPrems.optionKey) {
                    preconditions.splice(idx, 1);
                    del = true;
                    break;
                }
            } else if (delPrems.sourceItem) {
                if (precondition.sourceItem === delPrems.sourceItem) {
                    preconditions.splice(idx, 1);
                    del = true;
                    break;
                }
            } else if (delPrems.scoreValueCalc) {
                if (delPrems.scoreValueCalc.type === precondition.scoreValueCalc.type && delPrems.scoreValueCalc.value === precondition.scoreValueCalc.value) {
                    preconditions.splice(idx, 1);
                    del = true;
                    break;
                }
            }
        }
        if (del) return del;
        for (let idx = 0; idx < preconditions.length; idx++) {
            const precondition = preconditions[idx];
            for (let i = 0; i < precondition.children.length; i++) {
                const child = precondition.children[i];
                del = Precondition.removeprecond(child.precondition, delPrems);
                if (del) break;
            }
            if (del) break;
        }
        return del;
    }
    getMaxScore(): number {
        if (this.sourceItem.isGroup) return 99999999;
        let score = 0;
        (<CustomFormItem>this.sourceItem).options.forEach(o => score += o.score);
        return score;
    }

    hisBugs(): boolean {
        let min = 0;
        let max = 0;
        if (!this.sourceItem.isGroup) {
            if (!(<CustomFormItem>this.sourceItem).options) return false;
            (<CustomFormItem>this.sourceItem).options.forEach((a, idx) => {
                if (!idx) min = a.score ? a.score : 0;
                else {
                    if (a.score < min) min = a.score;
                }
                max += a.score;
            });
            if (this.scoreValueCalc.type === '<' && this.scoreValueCalc.value >= max || this.scoreValueCalc.value <= min) return true;
            if (this.scoreValueCalc.type === '=' && (<CustomFormItem>this.sourceItem).options.findIndex(f => f.score === this.scoreValueCalc.value) === -1) return true;
            if (this.scoreValueCalc.type === '>' && this.scoreValueCalc.value <= min || this.scoreValueCalc.value >= max) return true;
        } else {
            return false;
        }
        return false;
    }

    removeItem(key: string, precondition: Precondition = this): boolean {
        let del = false;
        for (let i = 0; i < precondition.children.length; i++) {
            const child = precondition.children[i];
            if (child.key === key) {
                precondition.children.splice(i, 1);
                del = true;
                break;
            }
        }
        if (del) return del;
        for (let i = 0; i < precondition.children.length; i++) {
            const child = precondition.children[i];
            if (child.group) del = child.group.removeItem(key);
            if (del) break;
            for (let idx = 0; child.precondition && idx < child.precondition.length; idx++) {
                const pre = child.precondition[idx];
                del = pre.removeItem(key);
                if (pre.children.length === 0) child.precondition.splice(idx, 1);
                if (del) break;
            }
        }
        return del;
    }

    isShow(): boolean {
        if (!this.sourceItem) return false;
        switch (this.type) {
            case 'score':
                return this.calcScore(this.sourceItem.score);
            case 'selected':
                const source = <CustomFormItem>this.sourceItem;
                if (source && !source.isGroup) {
                    return this.OptionKey.checked || source.value === this.OptionKey.key;
                }
                return false;
            default:
                return false;
        }
    }
    calcScore(score: number): boolean {
        switch (this.scoreValueCalc.type) {
            case '<':
                return score < this.scoreValueCalc.value;
            case '>':
                return score > this.scoreValueCalc.value;
            case '=':
                return score === this.scoreValueCalc.value;
            default:
                return false;
        }
    }
    toJson(hasValue = false): IPrecondition {
        return {
            type: this.type,
            children: this.children.map(v => v.toJson(hasValue)),
            scoreValueCalc: this.scoreValueCalc,
            OptionKey: this.OptionKey ? { key: this.OptionKey.key } : undefined
        };
    }
}
export class CustomFormListItem implements ICustomFormListItem {
    constructor(body: CustomFormGroup | CustomFormItem) {
        if (body.isGroup) {
            // this.isGroup = body.isGroup;
            this.group = body;
        } else {
            this.item = <CustomFormItem>body;
        }
        this.isGroup = body.isGroup;
        this.key = body.key;
    }
    key: string;
    isGroup: boolean;
    precondition?: Precondition[] = [];
    group?: CustomFormGroup;
    item?: CustomFormItem;

    static fromJson(data: ICustomFormListItem, root: CustomFormGroup): CustomFormListItem {
        if (data.isGroup) {
            // tslint:disable-next-line: no-use-before-declare
            const fromListItem = new CustomFormListItem(CustomFormGroup.fromJson(data.group, root));
            // fromListItem.key = data.key;
            // fromListItem.isGroup = data.isGroup;
            fromListItem.precondition = data.precondition && data.precondition.map(v => Precondition.fromJson(v, fromListItem.group));
            return fromListItem;
        } else {
            const fromListItem = new CustomFormListItem(CustomFormItem.fromJson(data.item));
            // fromListItem.key = data.key;
            // fromListItem.isGroup = data.isGroup;
            fromListItem.precondition = data.precondition && data.precondition.map(v => Precondition.fromJson(v, fromListItem.item));
            if (fromListItem.precondition) {
                fromListItem.precondition.forEach(v => {
                    if (!v.sourceItem.isGroup)
                        v.OptionKey = (<CustomFormItem>(v.sourceItem)).options.find(f => f.key === v.OptionKey.key);
                });
            }
            return fromListItem;
        }
    }
    toJson(hasValue = false): ICustomFormListItem {
        return {
            key: this.key,
            isGroup: this.isGroup,
            precondition: this.precondition.filter(f => f.children && f.children.length > 0).map(a => a.toJson(hasValue)),
            group: this.group && this.group.toJson(hasValue),
            item: this.item && this.item.toJson(hasValue)
        };
    }
}
export class CustomFormGroup implements ICustomFormGroup {
    static root: CustomFormGroup;
    key?: string;
    isGroup: true = true;
    groupName: string;
    maxScore?: number;
    isImport?: boolean;
    importOption?: { id: number; number: number; name: string };
    score?: number;
    readonly itemList: CustomFormListItem[] = [];
    root: CustomFormGroup;
    selectedGroupKey: string;
    selectedGroup: CustomFormGroup;
    selectedItemKey: string;
    /**
     * 将储存的 ICustomFormGroup 格式的json转换为 CustomFormGroup 对象
     */
    static fromJson(data: ICustomFormGroup, root?: CustomFormGroup): CustomFormGroup {
        const group = new CustomFormGroup(data.groupName);
        const itemList = data.itemList;
        data.itemList = [];
        Object.assign(group, data);
        if (root) group.root = root;
        else group.root = group;
        group.itemList.push(...itemList.map(v => CustomFormListItem.fromJson(v, group.root)));
        return group;
    }
    constructor(name?: string, root?: CustomFormGroup) {
        if (typeof name === 'string' && name !== 'default') {
            this.groupName = name;
            if (root) {
                this.root = root;
                this.root.selectedGroup = this;
                this.root.selectedGroupKey = this.key;
            }

        } else {
            this.groupName = 'default';
            this.key = '1';
            this.root = this;
            this.root.selectedGroup = this;
            this.root.selectedGroupKey = this.key;
        }

    }
    /**
     * 获取用于储存的json
     */
    toJson(hasValue = false): ICustomFormGroup {
        return {
            key: this.key,
            isGroup: this.isGroup,
            groupName: this.groupName,
            maxScore: this.maxScore,
            isImport: this.isImport,
            importOption: this.importOption,
            itemList: this.itemList.map(a => a.toJson(hasValue)),
        };
    }
    addGroup(name?: string): CustomFormGroup {
        const group = new CustomFormGroup(name, this.root);
        this.pushListItem(new CustomFormListItem(group));
        return group;
    }
    addItem(type: itemType): CustomFormItem {
        const item = new CustomFormItem(type);
        this.pushListItem(new CustomFormListItem(item));
        return item;
    }
    pushListItem(listItem: CustomFormListItem) {
        const key = this.getKey();
        listItem.key = key;
        if (listItem.isGroup) {
            listItem.group.key = key;
            this.setSelected(listItem.group);
            listItem.group.reSetKey();
        } else {
            listItem.item.key = key;
            this.setSelected(listItem.item);
            listItem.item.reSetKey();
        }
        this.itemList.push(listItem);
    }
    /**
     * 导入分组
     */
    importTemplate(item?: ICustomFormGroup) {
        const group = CustomFormGroup.fromJson(item, this.root); // new CustomFormGroup(item);
        this.pushListItem(new CustomFormListItem(group));
    }
    /**
     * 导入题目
     */
    ImportTitle(title: ICustomFormItem) {
        const item = CustomFormItem.fromJson(title); // new CustomFormItem(title);
        this.pushListItem(new CustomFormListItem(item));
    }
    /**
     * 重新设置key
     */
    reSetKey() {
        this.itemList.forEach((v, i) => {
            v.key = this.key + '.' + i.toString().padStart(3, '0');
            if (v.isGroup) {
                v.group.key = v.key;
                v.group.reSetKey();
            } else {
                v.item.key = v.key;
                v.item.reSetKey();
            }
            if (v.precondition) v.precondition.forEach((p, idx) => reset(p.children, v.key + '.pre' + idx + '.'));
        });
        function reset(children: CustomFormListItem[], pk: string) {
            children.forEach((v, i) => {
                v.key = pk + i.toString().padStart(3, '0');
                if (v.isGroup) {
                    v.group.key = v.key;
                    v.group.reSetKey();
                } else {
                    v.item.key = v.key;
                    v.item.reSetKey();
                }
                if (v.precondition) {
                    v.precondition.forEach((p, idx) => reset(p.children, v.key + '.pre' + idx + '.'));
                }
            });
        }
    }
    setSelected(item: CustomFormGroup | CustomFormItem) {
        setTimeout(() => {
            if (item.isGroup) {
                this.root.selectedGroupKey = item.key;
                this.root.selectedGroup = item;
            } else {
                this.root.selectedItemKey = item.key;
                this.root.selectedGroupKey = this.key;
                this.root.selectedGroup = this;
            }
        }, 0);
    }
    /**
     * 获取当前组的列表的下一个key
     */
    getKey(): string {
        return this.key + '.' + (this.itemList.length + 1).toString().padStart(3, '0');
    }

    /**
     * 前置条件bug检查 表单项/组可用性检查
     */
    itemBugExamine(item: CustomFormListItem) {
        // 检查重复key或缺失key
        // 检查前置条件为分数的是不是无效条件 条件需要的分值对于题目来说达不到
        // 检查无效题目和选项

    }
    /**
     * 检查是否填写完成
     */
    fixRequird(): CustomFormItem | undefined {
        let item: CustomFormItem;
        for (let idx = 0; idx < this.itemList.length; idx++) {
            const element = this.itemList[idx];
            if (!element.isGroup) {
                if (element.item.isRequird) {
                    if (!element.item.value && !element.item.files.length) {
                        item = element.item;
                        break;
                    }
                }
            }
        }
        if (item) return item;
        for (let idx = 0; idx < this.itemList.length; idx++) {
            const element = this.itemList[idx];
            if (element.isGroup) {
                item = element.group.fixRequird();
                if (item) break;
            }
        }
        if (item) return item;
        for (let idx = 0; idx < this.itemList.length; idx++) {
            const element = this.itemList[idx];
            if (element.precondition) {
                for (let i = 0; i < element.precondition.length; i++) {
                    const pre = element.precondition[i];
                    if (pre.isShow()) {
                        for (let ci = 0; ci < pre.children.length; ci++) {
                            const child = pre.children[ci];
                            if (!child.isGroup) {
                                if (child.item.isRequird) {
                                    if (!child.item.value && !child.item.files.length) {
                                        item = child.item;
                                    }
                                }
                            }
                            if (item) return item;
                        }
                        for (let ci = 0; ci < pre.children.length; ci++) {
                            const child = pre.children[ci];
                            if (child.isGroup) {
                                item = element.group.fixRequird();
                            }
                            if (item) return item;
                        }
                    }
                }
                item = element.group.fixRequird();
                if (item) break;
            }
        }
        return item;
    }
    /**
     * 获取group下全部的题目和答案 包含分数和group名称
     * @param group group
     */
    getQuestionAndAnswer(group: CustomFormGroup = this.root,
        parentGroupName?: string): { groupName: string, question: string, answer: string, score: number, supplement: string, files: AttachmentEditDto[] }[] {
        const list: {
            groupName: string,
            question: string,
            answer: string,
            score: number,
            supplement: string,
            files: AttachmentEditDto[]
        }[] = [];
        if (!group) return list;
        let groupName = group.groupName === 'default' ? '' : group.groupName;
        if (parentGroupName) groupName = parentGroupName + '_' + groupName;
        group.itemList.forEach(v => {
            if (v.isGroup) list.push(...this.getQuestionAndAnswer(v.group, groupName));
            else if (!v.isGroup) {
                const score = getScore(v.item);
                list.push(...getAnswer(v.item).map(m => ({
                    groupName: groupName,
                    question: v.item.title,
                    answer: m,
                    score: score,
                    supplement: v.item.supplement ? v.item.supplementValue : undefined,
                    files: v.item.files
                })));
            }
            if (v.precondition) {
                v.precondition.forEach(p => {
                    if (p.isShow()) {
                        p.children.forEach(c => {
                            if (c.isGroup) list.push(...this.getQuestionAndAnswer(c.group, groupName));
                            else if (!c.isGroup) {
                                const score = getScore(c.item);
                                list.push(...getAnswer(c.item).map(m => ({
                                    groupName: groupName,
                                    question: c.item.title,
                                    answer: m,
                                    score: score,
                                    supplement: c.item.supplement ? c.item.supplementValue : undefined,
                                    files: c.item.files
                                })));
                            }
                        });
                    }
                });
            }
        });

        function getAnswer(item: CustomFormItem): string[] {
            const result: string[] = [];
            let opt;
            switch (item.type) {
                case 'radio':
                    opt = item.options.find(f => f.key === item.value);
                    if (opt) result.push(opt.title);
                    break;
                case 'ckeckBox':
                    item.options.forEach(v => {
                        if (v.checked) result.push(v.title);
                    });
                    break;
                case 'blank':

                    break;
                case 'text-content':

                    break;
                default:
                    result.push(item.value + '');
                    break;
            }
            return result;
        }

        function getScore(item: CustomFormItem): number {
            let score = 0;
            let opt: {
                key: string;
                title: string;
                checked: boolean;
                disable: boolean;
                score?: number;
            };
            switch (item.type) {
                case 'radio':
                    opt = item.options.find(f => f.key === item.value);
                    if (opt) score += opt.score ? opt.score : 0;
                    break;
                case 'ckeckBox':
                    item.options.forEach(v => {
                        if (v.checked) score += v.score ? v.score : 0;
                    });
                    break;
                default:
                    break;
            }
            return score;
        }
        return list;
    }
    /**
     * 通过key查找
     */
    findItemByKey(key: string, group: CustomFormGroup = this.root): CustomFormGroup | CustomFormItem | undefined {
        for (let i = 0, l = group.itemList.length; i < l; i++) {
            const item = group.itemList[i];
            if (item.key === key) return item.isGroup ? item.group : item.item;
            if (item.precondition) {
                let res: CustomFormGroup | CustomFormItem | undefined;
                for (let index = 0; index < item.precondition.length; index++) {
                    for (let ci = 0; ci < item.precondition[index].children.length; ci++) {
                        if (item.precondition[index].children[ci].key === key) {
                            res = item.precondition[index].children[ci].isGroup ? item.precondition[index].children[ci].group : item.precondition[index].children[ci].item;
                            break;
                        } else if (item.precondition[index].children[ci].isGroup) {
                            res = this.findItemByKey(key, item.precondition[index].children[ci].group);
                            if (res) break;
                        }
                    }
                }
                if (res) return res;
            }
            if (item.isGroup) return this.findItemByKey(key, item.group);
        }
        return group.groupName === 'default' ? undefined : group;
    }
    /**
     * 通过带层级的key数组查找
     */
    // getItemBySourceKey(sourceKey: string[], group: CustomFormGroup = this.root): CustomFormGroup | CustomFormItem | undefined {
    //     let item: CustomFormGroup | CustomFormItem = group;
    //     sourceKey.forEach(key => {
    //         const v = item.isGroup && item.itemList.find(k => k.key === key);
    //         if (v) item = v.item;
    //     });
    //     return item.groupName === 'default' ? undefined : item;
    // }

    /**
     * 删除分组或题目
     */
    removeItem(key: string, group: CustomFormGroup = this): boolean {
        console.log(key, this);
        const item: CustomFormGroup | CustomFormItem = group;
        if (!key || !group || !group.isGroup) return;
        if (key === item.key && item.isGroup && item.groupName === 'default') return;
        let del = false;
        for (let i = 0, l = group.itemList.length; i < l; i++) {
            const val = group.itemList[i];
            if (val.key === key) {
                group.itemList.splice(i, 1);
                del = true;
                break;
            }
        }
        if (!del)
            for (let i = 0, l = group.itemList.length; i < l; i++) {
                const val = group.itemList[i];
                if (val.isGroup) del = this.removeItem(key, val.group);
                if (del) break;
                for (let index = 0; val.precondition && index < val.precondition.length; index++) {
                    del = val.precondition[index].removeItem(key);
                    if (val.precondition[index].children.length === 0) {
                        val.precondition = undefined;
                    }
                    if (del) break;
                }
                if (del) break;
            }
        group.reSetKey();
        return del;

    }
}
